package com.google.code.b0rx0r;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.swing.JOptionPane;

import jvst.wrapper.VSTPluginAdapter;
import jvst.wrapper.valueobjects.VSTEvent;
import jvst.wrapper.valueobjects.VSTEvents;
import jvst.wrapper.valueobjects.VSTMidiEvent;
import jvst.wrapper.valueobjects.VSTTimeInfo;

import com.google.code.b0rx0r.advancedSamplerEngine.AudioProcessor;
import com.google.code.b0rx0r.program.Program;
import com.google.code.b0rx0r.program.ProgramManager;

public class B0rx0r extends VSTPluginAdapter implements TempoProvider {
	
	public static final int NUMBER_OF_STEREO_OUTS = 8;
	
	private ProgramManager programManager = new ProgramManager(this);
	private AudioProcessor processor = new AudioProcessor();
	private PlayQueue queue = new PlayQueue(programManager, processor);
	
	private float tempo;
	
	public B0rx0r(long Wrapper) {
		super(Wrapper);
		
    this.setNumInputs(0);// no input
    this.setNumOutputs(2 * NUMBER_OF_STEREO_OUTS);// 8 stereo outputs or other configuration
    this.canProcessReplacing(true);
    this.isSynth(true);
    this.setUniqueID("b0rx0r".hashCode()); //$NON-NLS-1$
    
    this.programsAreChunks(true);

    updateTempo();

    this.wantEvents(1);
    this.suspend();
	}
	
	private void updateTempo() {
    VSTTimeInfo ti = this.getTimeInfo(VSTTimeInfo.VST_TIME_TEMPO_VALID);
    
    if (ti != null) {
     if ((ti.getFlags() & VSTTimeInfo.VST_TIME_TEMPO_VALID) !=0) {
      this.tempo = (float)ti.getTempo();
     }
    }
	}

	@Override
	public int canDo(String feature) {
    int ret = CANDO_NO;

    if (CANDO_PLUG_RECEIVE_VST_EVENTS.equals(feature)) ret = CANDO_YES;
    if (CANDO_PLUG_RECEIVE_VST_MIDI_EVENT.equals(feature)) ret = CANDO_YES;
    if (CANDO_PLUG_MIDI_PROGRAM_NAMES.equals(feature)) ret = CANDO_YES;

    return ret;
	}


	@Override
	public int getPlugCategory() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getProductString() {
		return "b0rx0r"; //$NON-NLS-1$
	}
	

	@Override
	public String getVendorString() {
		return "wg"; //$NON-NLS-1$
	}
	
	

	@Override
	public void processReplacing(float[][] inputs, float[][] outputs, int sampleFrames) {
		updateTempo();
		processor.processReplacing(inputs, outputs, sampleFrames);
	}

	
	
	 public void resume() { 
		  this.wantEvents(1); //deprecated as of vst2.4 
		  					  //but keep it --> backward compatibility!!!
	  }

	
	@Override
	public int processEvents(VSTEvents e) {
		for (VSTEvent ve : e.getEvents()) {
      if (ve.getType() != VSTEvent.VST_EVENT_MIDI_TYPE) continue;

      VSTMidiEvent event = (VSTMidiEvent)ve;
      byte[] midiData = event.getData();
      int status = midiData[0] & 0xf0;// ignoring channel

      if (status == 0x90 || status == 0x80) {
        // we only look at notes
        int note = midiData[1] & 0x7f;
        int velocity = midiData[2] & 0x7f;
       
        if (status == 0x80 || velocity == 0) {
        	queue.noteOff(note);
        } else {
        	queue.noteOn(note, velocity);
        }
      }
      else if (status == 0xb0) {
        // all notes off
        if (midiData[1] == 0x7e || midiData[1] == 0x7b)	
        	queue.allNotesOff();
      }
    }

		
		return 1;	// want more
	}

	@Override
	public boolean setBypass(boolean arg0) {
		// TODO Auto-generated method stub
		return false;
	}

	
	
	
	
	
	
	
	public PlayQueue getQueue() {
		return queue;
	}

	public AudioProcessor getProcessor() {
		return processor;
	}

	public ProgramManager getProgramManager() {
		return programManager;
	}


	
	
	
	@Override
	public int getNumPrograms() {
		return programManager.getProgramCount();
	}

	@Override
	public int getProgram() {
		return programManager.getCurrentProgramIndex();
	}

	@Override
	public String getProgramName() {
		return programManager.getProgram().getName();
	}

	@Override
	public String getProgramNameIndexed(int category, int index) {
		return programManager.getProgram(index).getName();
	}


	@Override
	public void setProgramName(String name) {
		programManager.getProgram().setName(name);

	}

	@Override
	public void setProgram(int index) {
		programManager.setCurrentProgramIndex(index);
	}


	/*
	 * using getChunk / setChunk

Type : plugin persistence with custom data
References : originally posted by Karl Steinberg

Notes :
[I took this from a post to the mailing list by Charlie. - Urs]

...the programmer labelled these methods from the host point of view. thus getChunk means 'dear plug please give me your data for persistent storage', while setChunk means 'hi plug here's what you gave me for storage, load it back in'.

[Annotation: if you havn't got the data already allocated in memory like this example demonstrates - if you need to assemble it for getChunk, that is -, it is save to free the pointer in resume() or on plugin destruction - Urs]

Code :
//--------------------------------------------------------------------------
struct MyPreset
{
        float gain;
};

//--------------------------------------------------------------------------
struct MyProjectData
{
        long version;
        MyPreset presets[kNumPrograms];
};

static MyProjectData projectData = {1, 1, {0}};

//--------------------------------------------------------------------------
long MyPlug::getChunk (void** data, bool isPreset)
{
    if (isPreset)
    {
        *data = &projectData.presets[currentProgram];
        return sizeof (MyPreset);
    }
    *data = &projectData;
    return sizeof (MyProjectData);
}

//--------------------------------------------------------------------------
long MyPlug::setChunk (void* data, long byteSize, bool isPreset)
{
    if (isPreset)
    {
        if (byteSize != sizeof (MyPreset))
            return 0;
        projectData.presets[currentProgram] = *(MyPreset*)data;
    }
    else
    {
        if (byteSize != sizeof (MyProjectData))
            return 0;
        projectData = *(MyProjectData*)data;
    }
    return 1;
}

(written off my head, no guarantee)
charlie

2 comment(s) | add a comment | nofrills version
	
		

				
	
using getChunk / setChunk: Addendum

Type : parameter persistence
References : Posted by Ged Grey

Notes :
It might be a good idea also to place a call to:
setProgram(curProgram); // new parameters will now kick in
as the last thing in your setChunk() method, as it is not certain whether the
host will actually call this for you.
Also, be sure to declare programsAreChunks(true) or whatever it's called
in your plug's constructor... (non-Javadoc)
	 * @see jvst.wrapper.VSTPluginAdapter#getChunk(byte[][], boolean)
	 */
	
	
	
	
	
	


	@Override
	public int getChunk(byte[][] data, boolean isPreset) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(programManager.getProgram());
			data[0] = baos.toByteArray();
			return data[0].length;
			
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public int setChunk(byte[] data, int byteSize, boolean isPreset) {
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
		try {
			ObjectInputStream ois = new ObjectInputStream(bais);
			Program program = (Program)ois.readObject();
			programManager.setProgram(program);
			//TODO should we set the currentrpogramid?
			
			return 1;
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, Messages.getString("B0rx0r.ChunkFormatError")); //$NON-NLS-1$
			e.printStackTrace();
			return 0;
		}
	}

	
	

	
	
	
	
	
	
	@Override
	public int getNumParams() {
		// TODO Auto-generated method stub
		return 0;
	}

	

	@Override
	public float getParameter(int arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getParameterDisplay(int arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getParameterLabel(int arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getParameterName(int arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	
	
	@Override
	public void setParameter(int arg0, float arg1) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean string2Parameter(int arg0, String arg1) {
		// TODO Auto-generated method stub
		return false;
	}

	
	
	
	
	@Override
	public float getTempo() {
		return tempo;
	}

	
	
}
